home *** CD-ROM | disk | FTP | other *** search
- /*
- sgopher.c - a very simple client for the internet gopher
-
- author : Sean Fuller (fuller@aedc-vax.af.mil)
- Arnold Engineering and Development Center
-
- This software is provided "as is" without express or
- implied warranty. In no event shall the author be liable
- for damages of any kind arising from or in connection
- with the use of this software.
-
- 4/27/1993 - Wrote and released initial version.
- 4/28/1993 - Added connection information logging and
- pulled configuration information out to
- the make file. Also added help command.
- Added ability to mail received text.
- 4/29/1993 - Added support for gopher indexes.
- Added ongoing menu download indication.
- Fixed empty menu retrieval.
- Moved ask up so not implicitly declared.
- 5/3/1993 - Gopher mail now from GOPHADDR and logged.
- Added 'Subject:' and 'To:' line in mail sent.
- 5/4/1993 - now porting to VMS
- 5/5/1993 - got it to compile and run on VMS but no items found
- getting readline errors on server
- 5/6/1993 - Modified for ULTRIX compilation.
- fixed it so that it doesn't exit on bad connections.
- fixed index retrieval.
- 5/7/1993 - Added code for doing timeout.
- 5/10/1993 - Added = option for retrieval information.
- */
-
- #include "conf.h"
-
- #ifdef VMS
- #define read netread
- #define write netwrite
- #endif
-
- #include <stdio.h>
- #include <ctype.h>
- #ifdef VMS
- #include <stdlib.h>
- #include "twg$tcp:[netdist.include.sys]types.h"
- #include "twg$tcp:[netdist.include.sys]socket.h"
- #include "twg$tcp:[netdist.include.sys]time.h"
- #include "twg$tcp:[netdist.include.netinet]in.h"
- #include "twg$tcp:[netdist.include.arpa]inet.h"
- #include "twg$tcp:[netdist.include]netdb.h"
- #else
- #include <sys/types.h>
- #include <sys/socket.h>
- #include <netinet/in.h>
- #include <arpa/inet.h>
- #include <netdb.h>
- #endif
- #include <string.h>
- #include <time.h>
- #ifdef TIMEOUT
- #ifdef VMS
- #include "twg$tcp:[netdist.include.sys]signal.h"
- #else
- #include <sys/signal.h>
- #endif
- #endif
-
- #define BUFSIZE 256
- char buf[BUFSIZE];
- char title[BUFSIZE];
- int fd;
- int lines_per_page = 22;
-
- struct gopher_object;
- struct gopher_object {
- char *name;
- char *path;
- char *host;
- char *port;
- char viewed;
- struct gopher_object *next;
- };
-
- struct stack_item;
- struct stack_item {
- struct gopher_object *item;
- int top;
- char title[BUFSIZE];
- struct stack_item *next;
- };
-
- struct gopher_object root_object =
- {
- "Root Connection", "", HOST, PORT, 0, NULL
- };
-
- struct stack_item *gopher_stack = NULL;
-
- #ifdef NEED_STRDUP
- char *
- #ifdef KRC
- strdup(s)
- char *s;
- #else
- strdup(char *s)
- #endif
- {
- char *d;
- d = malloc(sizeof(char) * (strlen(s) + 1));
- if (d == NULL)
- {
- printf("Error: out of memory\n");
- exit(0);
- }
- strcpy(d, s);
- return d;
- }
- #endif
-
- #ifdef TIMEOUT
- /*
- handle_sigalrm - received SIGALRM signal, do cleanup and exit.
- */
- void
- #ifdef KRC
- handle_alarm(sig, code, sc)
- int sig, code;
- struct sigcontext *sc;
- #else
- handle_alarm(int sig, int code, struct sigcontext *sc)
- #endif
- {
- printf("\r\n");
- printf("Inactive for %d minute%s. ", TIMEOUT, (TIMEOUT > 1)?"s":"");
- printf("Terminating Gopher client...\r\n");
- exit(0);
- }
- #endif
-
- /*
- user_input - get string from the user - may use timeout
- */
- char *
- #ifdef KRC
- user_input(buf, bufsize, fp)
- char *buf;
- int bufsize;
- FILE *fp;
- #else
- user_input(char *buf, int bufsize, FILE *fp)
- #endif
- {
- char *s;
- #ifdef TIMEOUT
- alarm(TIMEOUT * 60); /* set timeout alarm */
- #endif
- s = fgets(buf, BUFSIZE, stdin);
- #ifdef TIMEOUT
- alarm(0); /* reset timeout alarm */
- #endif
- #ifdef VMS
- printf("\n");
- #endif
- return s;
- }
-
- /*
- user_wait - wait for user to press enter
- */
- void user_wait()
- {
- printf("press <return> to continue: ");
- fflush(stdout);
- user_input(buf, BUFSIZE, stdin);
- }
-
- /*
- find_item - return item in menu given number
- */
- struct gopher_object *
- #ifdef KRC
- find_item(g, num)
- struct gopher_object *g;
- char *num;
- #else
- find_item(struct gopher_object *g, char *num)
- #endif
- {
- int i;
- i = atoi(num);
- if (i <= 0)
- {
- printf("Item numbers must be greater than 0.\r\n");
- user_wait();
- return NULL;
- }
- while (i > 1 && g)
- {
- i--;
- g = g->next;
- }
- if (g == NULL)
- {
- printf("Item number is too big.\r\n");
- user_wait();
- return NULL;
- }
- return g;
- }
-
- #ifdef LOGFILE
- /*
- get_client_info - get the ip address and hostname of originator
- and write it out to the log file
- */
- void get_client_info()
- {
- char ipnum[256];
- char name[256];
- char logtime[256];
- struct sockaddr_in sa;
- int length;
- int rc;
- FILE *fp;
- time_t the_time;
- u_long net_addr;
- struct hostent *hp;
- length = sizeof(sa);
- rc = getpeername(0, &sa, &length);
- if (rc != 0) strcpy(ipnum, "localhost");
- else strcpy(ipnum, inet_ntoa(sa.sin_addr));
- hp = gethostbyaddr(&sa.sin_addr, sizeof(sa.sin_addr), AF_INET);
- the_time = time(0);
- strcpy(logtime, ctime(&the_time));
- if (strlen(logtime) > 0) logtime[strlen(logtime) - 1] = 0;
- if (hp != NULL)
- {
- printf("Connected from %s(%s) at %s\r\n", hp->h_name, ipnum,
- logtime);
- strcpy(name, hp->h_name);
- }
- else
- {
- printf("Connected from %s at %s\r\n", ipnum, logtime);
- strcpy(name, "unknown");
- }
- fp = fopen(LOGFILE, "a");
- if (fp == NULL) return;
- fprintf(fp, "%s %s %s\n", logtime, ipnum, name);
- fclose(fp);
- }
- #endif
-
- /*
- push_menu - push menu and top item on stack
- */
- void
- #ifdef KRC
- push_menu(g, top)
- struct gopher_object *g;
- int top;
- #else
- push_menu(struct gopher_object *g, int top)
- #endif
- {
- struct stack_item *s;
- s = (struct stack_item *)malloc(sizeof(struct stack_item));
- if (s == NULL)
- {
- printf("Error: out of memory\r\n");
- return;
- }
- s->item = g;
- s->top = top;
- s->next = gopher_stack;
- strcpy(s->title, title);
- gopher_stack = s;
- }
-
- /*
- pop_menu - pop previous menu and top item from stack
- */
- struct gopher_object *
- #ifdef KRC
- pop_menu(top)
- int *top;
- #else
- pop_menu(int *top)
- #endif
- {
- struct gopher_object *g;
- struct stack_item *i;
- if (gopher_stack == NULL) return NULL;
- i = gopher_stack;
- g = i->item;
- gopher_stack = i->next;
- *top = i->top;
- strcpy(title, i->title);
- free(i);
- return g;
- }
-
- /*
- readport - read a line in from the socket to the gopher server
- */
- int readport()
- {
- int i, l;
- char *s = buf;
- l = 0;
- while ((i = read(fd, s, 1)) > 0)
- {
- l += i;
- if (*s == '\n') break;
- if (*s != '\r') s++;
- }
- *s = 0;
- return l;
- }
-
- /*
- close_port - close the connection to the gopher server
- */
- void close_port()
- {
- close(fd);
- }
-
- /*
- ask - make the connection to the gopher server and request item
- */
- int
- #ifdef KRC
- ask(g)
- struct gopher_object *g;
- #else
- ask(struct gopher_object *g)
- #endif
- {
- struct sockaddr_in sin;
- struct hostent *host;
- int port;
- printf("Connecting...\r\n");
- fflush(stdout);
- bzero((char*)&sin, sizeof(sin));
- port = atoi(g->port);
- sin.sin_port = htons(port);
- host = gethostbyname(g->host);
- if (host == NULL)
- {
- sin.sin_addr.s_addr = inet_addr(g->host);
- }
- else
- {
- memcpy(&sin.sin_addr.s_addr, host->h_addr_list[0],
- host->h_length);
- }
- sin.sin_family = AF_INET;
- fd = socket(AF_INET, SOCK_STREAM, 0);
- if (fd < 0)
- {
- printf("Error: gopher socket unavailable\r\n");
- return 0;
- }
- if (connect(fd, (struct sockaddr *)&sin, sizeof(sin)) < 0)
- {
- printf("Error: gopher server unavailable\r\n");
- return 0;
- }
- if (write(fd, g->path, strlen(g->path)) == -1)
- {
- printf("Error: write error\r\n");
- return 0;
- }
- if (write(fd, "\n", 1) == -1)
- {
- printf("Error: write error\r\n");
- return 0;
- }
- return 1;
- }
-
- /*
- mail - Mail gopher item to given email address.
- There is no check to see if it worked.
- */
- void
- #ifdef KRC
- mail(g, email)
- struct gopher_object *g;
- char *email;
- #else
- mail(struct gopher_object *g, char *email)
- #endif
- {
- char buf[BUFSIZE];
- struct sockaddr_in sin;
- struct hostent *host;
- char c;
- int mfd;
- FILE *fp;
- /*
- open socket to mailer port on gopher host
- */
- printf("Sending Mail...\r\n");
- fflush(stdout);
- bzero((char*)&sin, sizeof(sin));
- sin.sin_port = htons(25);
- host = gethostbyname(HOST);
- if (host == NULL)
- {
- sin.sin_addr.s_addr = inet_addr(g->host);
- }
- else
- {
- memcpy(&sin.sin_addr.s_addr, host->h_addr_list[0],
- host->h_length);
- }
- sin.sin_family = AF_INET;
- mfd = socket(AF_INET, SOCK_STREAM, 0);
- if (mfd < 0)
- {
- printf("Error: mail socket unavailable\r\n");
- return;
- }
- if (connect(mfd, (struct sockaddr *)&sin, sizeof(sin)) < 0)
- {
- printf("Error: mail server unavailable\r\n");
- return;
- }
- /*
- send header information
- */
- sprintf(buf, "HELO %s\n", HOST);
- write(mfd, buf, strlen(buf));
- sprintf(buf, "MAIL From:<%s>\n", GOPHADDR);
- write(mfd, buf, strlen(buf));
- sprintf(buf, "RCPT To:<%s>\n", email);
- write(mfd, buf, strlen(buf));
- sprintf(buf, "DATA\n");
- write(mfd, buf, strlen(buf));
- sprintf(buf, "To: %s\n", email);
- write(mfd, buf, strlen(buf));
- sprintf(buf, "Subject: Gopher: %s\n", g->name + 1);
- write(mfd, buf, strlen(buf));
- if (ask(g))
- {
- while (read(fd, &c, 1) > 0)
- {
- write(mfd, &c, 1);
- }
- close_port();
- }
- sprintf(buf, "\n.\n");
- write(fd, buf, strlen(buf));
- sprintf(buf, "BYE\n");
- write(fd, buf, strlen(buf));
- printf("Message sent.\r\n");
- fflush(stdout);
- close(mfd);
- #ifdef LOGFILE
- /*
- Log mail sent.
- */
- fp = fopen(LOGFILE, "a");
- if (fp == NULL) return;
- fprintf(fp, "Sending %s %s %s to %s\n",
- g->path, g->host, g->port, email);
- fclose(fp);
- #endif
- }
-
- /*
- parse_menu_item - parse the menu item returned by gopher
- */
- struct gopher_object *
- #ifdef KRC
- parse_menu_item(buf)
- char *buf;
- #else
- parse_menu_item(char *buf)
- #endif
- {
- struct gopher_object *g;
- char *s[4];
- int i;
- char *p;
- g = (struct gopher_object *)malloc(sizeof(struct gopher_object));
- if (g == NULL)
- {
- printf("Error: out of memory\r\n");
- return NULL;
- }
- p = buf;
- i = 0;
- while (*p && i < 4)
- {
- s[i] = p;
- while (*p && *p != '\t') p++;
- if (*p)
- {
- *p = 0;
- p++;
- i++;
- }
- }
- g->name = strdup(s[0]);
- g->path = strdup(s[1]);
- g->host = strdup(s[2]);
- g->port = strdup(s[3]);
- g->next = NULL;
- g->viewed = ' ';
- if (strlen(g->name) > 65) g->name[65] = 0;
- return g;
- }
-
- /*
- get_text - Get text document and display it.
- The gopher port is kept open during reading.
- Lines are truncated to 79 columns.
- */
- void
- #ifdef KRC
- get_text(g)
- struct gopher_object *g;
- #else
- get_text(struct gopher_object *g)
- #endif
- {
- char input[BUFSIZE];
- int i;
- if (ask(g) == 0) return;
- printf("Getting text...\r\n");
- fflush(stdout);
- i = -1;
- while (readport() > 0 && strcmp(buf, "."))
- {
- i++;
- if (i > lines_per_page)
- {
- printf("press <enter> for more or <q> to quit: ");
- fflush(stdout);
- user_input(input, BUFSIZE, stdin);
- if (*input == 'q') break;
- i = 0;
- printf("\r\n");
- }
- buf[79] = 0;
- printf("%s\r\n", buf);
- }
- printf("press <enter> to continue or <m> to mail: ");
- fflush(stdout);
- user_input(input, BUFSIZE, stdin);
- close_port();
- if (*input == 'm')
- {
- printf("enter email address: ");
- fflush(stdout);
- user_input(input, BUFSIZE, stdin);
- for (i=strlen(input) - 1; i>=0; i--)
- {
- if (input[i] == '\r' || input[i] == '\n')
- input[i] = 0;
- }
- mail(g, input);
- }
- }
-
- /*
- get_menu - given path get menu
- */
- struct gopher_object *
- #ifdef KRC
- get_menu(g)
- struct gopher_object *g;
- #else
- get_menu(struct gopher_object *g)
- #endif
- {
- char group[BUFSIZE];
- int i;
- struct gopher_object *first = NULL;
- struct gopher_object *current = NULL;
- struct gopher_object *new;
- if (ask(g))
- {
- printf("Getting directory...");
- fflush(stdout);
- while (readport() > 0 && strcmp(buf, ".") && strlen(buf) > 0)
- {
- printf(".");
- fflush(stdout);
- new = parse_menu_item(buf);
- if (first)
- {
- current->next = new;
- current = new;
- }
- else
- {
- first = current = new;
- }
- }
- close_port();
- printf("\r\n");
- }
- if (first == NULL)
- {
- first = pop_menu(&i);
- printf("\r\n");
- printf("No menu items available.\r\n");
- user_wait();
- }
- return first;
- }
-
- /*
- showmenu - show a menu given pointer to linked list
- */
- void
- #ifdef KRC
- show_menu(g, top)
- struct gopher_object *g;
- int top;
- #else
- show_menu(struct gopher_object *g, int top)
- #endif
- {
- int i = 1;
- char *object_type;
- printf("\r\n");
- printf("%s\r\n", title);
- if (g == NULL)
- {
- printf("No items available\r\n");
- }
- while (g)
- {
- if (g->name[0] == '0') object_type = "text";
- else if (g->name[0] == '1') object_type = "dir";
- else if (g->name[0] == '7') object_type = "indx";
- else if (g->name[0] == '3') object_type = "err";
- else object_type = "?";
- if (i >= top) printf("%3d) %c%-65s [%s]\r\n", i, g->viewed,
- g->name + 1, object_type);
- if (i - top >= lines_per_page - 1) break;
- i++;
- g = g->next;
- }
- printf("Enter #=select p=previous page n=next page u=uplevel ?=help q=quit: ");
- }
-
- /*
- next_page - find item number of next page or back to top if last
- */
- int
- #ifdef KRC
- next_page(g, top)
- struct gopher_object *g;
- int top;
- #else
- next_page(struct gopher_object *g, int top)
- #endif
- {
- int i;
- i = 1;
- while (g && i < top)
- {
- i++;
- g = g->next;
- }
- if (g)
- {
- if (g->next == NULL)
- {
- return 1;
- }
- for (i=0; i<lines_per_page && g->next; i++)
- {
- top++;
- g = g->next;
- }
- }
- return top;
- }
-
- /*
- select_object - select an item by number
- */
- struct gopher_object *
- #ifdef KRC
- select_object(buf, menu, top)
- char *buf;
- struct gopher_object *menu;
- int *top;
- #else
- select_object(char *buf, struct gopher_object *menu,
- int *top)
- #endif
- {
- int i;
- char input[BUFSIZE];
- struct gopher_object *g;
- struct gopher_object *go;
- g = menu;
- if ((g = find_item(menu, buf)) == NULL) return menu;
- g->viewed = '*';
- if (g->name[0] == '1')
- {
- push_menu(menu, *top);
- if (strlen(g->name) > 1) strcpy(title, g->name + 1);
- g = get_menu(g);
- if (g)
- {
- *top = 1;
- return g;
- }
- return menu;
- }
- if (g->name[0] == '0') get_text(g);
- else if (g->name[0] == '7')
- {
- printf("\r\n");
- i = strlen(g->name);
- if (i > 1) printf("%s\r\n", g->name + 1);
- printf("Enter argument(s): ");
- fflush(stdout);
- i = strlen(g->path);
- strcpy(input, g->path);
- strcat(input, "\t");
- user_input(input + i + 1, BUFSIZE - i - 2, stdin);
- if (strlen(input) > i + 1)
- {
- go = (struct gopher_object *)malloc(
- sizeof(struct gopher_object));
- if (go == NULL)
- {
- printf("Error: out of memory\r\n");
- return menu;
- }
- go->name = strdup(g->name);
- go->path = strdup(input);
- go->host = strdup(g->host);
- go->port = strdup(g->port);
- push_menu(menu, *top);
- strcpy(title, g->name + 1);
- g = get_menu(go);
- free(go);
- if (g)
- {
- *top = 1;
- return g;
- }
- return menu;
- }
- }
- else
- {
- printf("\r\n");
- printf("The format of the Gopher item that you have selected cannot be retrieved\r\n");
- printf("or displayed by this gopher client. It is probably a binary file that\r\n");
- printf("contains a picture, music, or program executables. To retrieve these types\r\n");
- printf("of files you will have to run a different gopher client. Examples of\r\n");
- printf("other clients that can retrieve these types of data are Xgopher which\r\n");
- printf("runs on UNIX machines and UTGopher which runs on PCs with PC/TCP.\r\n");
- user_wait();
- }
- return menu;
- }
-
- /*
- free_directory - free gopher_objects in menu
- */
- void
- #ifdef KRC
- free_directory(menu)
- struct gopher_object *menu;
- #else
- free_directory(struct gopher_object *menu)
- #endif
- {
- struct gopher_object *g;
- while (menu)
- {
- g = menu;
- menu = menu->next;
- free(g->name);
- free(g->host);
- free(g->path);
- free(g->port);
- free(g);
- }
- }
-
- /*
- help - show help information
- */
- void help()
- {
- char buf[BUFSIZE];
- printf("\r\n");
- printf(" SIMPLE GOPHER CLIENT HELP\r\n");
- printf("\r\n");
- printf("dir - a directory of items that you can choose from\r\n");
- printf("text - a text document you can view or email\r\n");
- printf("indx - index type will request input and retrieve a directory\r\n");
- printf("err - an error that was returned by a server\r\n");
- printf("? - is displayed for all other types\r\n");
- printf("\r\n");
- printf("command desc\r\n");
- printf("------------- -----------------------------------------------------------\r\n");
- printf("<n><enter> type a number then return to fetch an item\r\n");
- printf("n<enter> to go to the next page of a menu\r\n");
- printf("<enter> to go to the next page of a menu\r\n");
- printf("p<enter> to go to the previous page of a menu\r\n");
- printf("?<enter> to display this information\r\n");
- printf("q<enter> to exit the gopher client\r\n");
- printf("=<n><enter> show retrieval information for item\r\n");
- printf("\r\n");
- #ifdef GOPHADM
- #ifdef GOPHPHN
- printf("If you need more help call %s at %s.\r\n",
- GOPHADM, GOPHPHN);
- #endif
- #endif
- user_wait();
- }
-
- /*
- show_obj_info - show information about gopher object # on menu
- */
- void
- #ifdef KRC
- show_obj_info(g, num)
- struct gopher_object *g;
- char *num;
- #else
- show_obj_info(struct gopher_object *g, char *num)
- #endif
- {
- int i;
- if ((g = find_item(g, num)) == NULL) return;
- printf("\r\n");
- printf("name: %s\r\n", g->name);
- printf("path: %s\r\n", g->path);
- printf("host: %s\r\n", g->host);
- printf("port: %s\r\n", g->port);
- printf("\r\n");
- user_wait();
- }
-
- /*
- main - parse the command line and call appropriate routines
- repeat until q<enter>
- */
- int
- #ifdef KRC
- main(argc, argv)
- int argc;
- char *argv;
- #else
- main(int argc, char *argv[])
- #endif
- {
- char buf[BUFSIZE];
- struct gopher_object *g;
- int top = 1;
- if (argc > 1) root_object.host = argv[1];
- if (argc > 2) root_object.port = argv[2];
- if (argc > 3)
- {
- printf("Format: gopher [host [port]]\r\n");
- }
- strcpy(title, root_object.name);
- #ifdef GOVCOMP
- printf("---------------------------------------------------------------------------\r\n");
- printf("WARNING!! THIS IS A GOVERNMENT OWNED COMPUTER.\r\n");
- printf("---------------------------------------------------------------------------\r\n");
- printf("GOVERNMENT TELECOMMUNICATIONS SYSTEMS AND AUTOMATED INFORMATION SYSTEMS ARE\r\n");
- printf("SUBJECT TO PERIODIC SECURITY TESTING AND MONITORING TO ENSURE PROPER\r\n");
- printf("COMMUNICATIONS SECURITY (COMSEC) PROCEDURES ARE BEING OBSERVED. USE OF THESE\r\n");
- printf("SYSTEMS CONSTITUTES CONSENT TO SECURITY TESTING AND COMSEC MONITORING.\r\n");
- printf("---------------------------------------------------------------------------\r\n");
- printf("FOR OFFICIAL USE ONLY.\r\n");
- printf("---------------------------------------------------------------------------\r\n");
- #endif
- #ifdef TIMEOUT
- signal(SIGALRM, handle_alarm);
- #endif
- fflush(stdout);
- #ifdef LOGFILE
- get_client_info();
- #endif
- g = get_menu(&root_object);
- while (1)
- {
- show_menu(g, top);
- fflush(stdout);
- user_input(buf, BUFSIZE, stdin);
- if (*buf == 'q') break;
- switch (*buf)
- {
- case '?':
- help();
- break;
- case 'u':
- free_directory(g);
- g = pop_menu(&top);
- if (g == NULL)
- {
- g = get_menu(&root_object);
- top = 1;
- }
- break;
- case 'p':
- top = (top > lines_per_page)
- ? top - lines_per_page : 1;
- break;
- case 'n': case '\n': case '\r': case 0:
- top = next_page(g, top);
- break;
- case '=':
- show_obj_info(g, buf + 1);
- break;
- default:
- g = select_object(buf, g, &top);
- }
- }
- return 0;
- }
-